package com.ittao.taopicturebackend.manager.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import com.ittao.taopicturebackend.config.CosClientConfig;
import com.ittao.taopicturebackend.exception.BusinessException;
import com.ittao.taopicturebackend.exception.ErrorCode;
import com.ittao.taopicturebackend.manager.cos.CosManager;
import com.ittao.taopicturebackend.model.dto.file.UploadPictureResult;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 图片上传模板方法（模板方法设计模式）
 */
@Slf4j
@Service
public abstract class PictureUploadTemplate {

    @Resource
    private CosClientConfig cosClientConfig;

    @Resource
    private CosManager cosManager;

    /**
     * 上传图片
     *
     * @param inputSource      上传的文件
     * @param uploadPathPrefix 上传路径前缀
     * @return
     */
    public UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {
        // 1. 校验图片
        validPicture(inputSource);
        // 2. 图片上传地址
        String uuid = RandomUtil.randomString(16);
        // 获取原始文件名
        String originalFilename = getOriginFilename(inputSource);
        // 自己拼接文件上传路径，而不是使用原始文件名称，可以增强安全性
        String uploadFileName = String.format("%s_%s.%s", DateUtil.formatDate(new Date()),
                uuid,
                (Objects.equals(FileUtil.getSuffix(originalFilename), "")
                        ? "png" : FileUtil.getSuffix(originalFilename)));
        String uploadPath = String.format("%s/%s", uploadPathPrefix, uploadFileName);
        // 解析结果并返回
        File file = null;
        try {
            // 3. 创建临时文件，上传文件到服务器
            file = File.createTempFile(uploadPath, null);
            // 处理文件来源
            processFile(inputSource, file);
            // 4. 上传到腾讯云COS
            PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);
            // 5. 获取图片信息对象，封装返回结果
            ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 6. 获取到图片处理结果
            ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
            List<CIObject> objectList = processResults.getObjectList();
            if (CollUtil.isNotEmpty(objectList)) {
                // 获取压缩后的图片对象
                CIObject compressCiObject = objectList.get(0);
                // 如果有缩略图，则获取缩略图的图片对象，否则使用压缩图作为缩略图
                CIObject thumbnailCiObject = compressCiObject;
                if (objectList.size() > 1) {
                    // 获取缩略图的图片对象
                    thumbnailCiObject = objectList.get(1);
                }
                // 封装压缩图的返回结果
                return buildResult(originalFilename, compressCiObject, thumbnailCiObject, imageInfo, inputSource);
            }
            // 返回原图的返回结果
            return buildResult(originalFilename, file, uploadPath, imageInfo, inputSource);
        } catch (IOException e) {
            log.error("文件上传失败,filePath = {}", uploadPath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件上传失败，请稍后重试");
        } finally {
            // 临时文件清理
            deleteTempFile(file);
        }
    }

    /**
     * 校验图片
     *
     * @param inputSource
     */
    protected abstract void validPicture(Object inputSource);

    /**
     * 获取原始文件名
     *
     * @param inputSource 上传的文件
     * @return 原始文件名
     */
    protected abstract String getOriginFilename(Object inputSource);

    /**
     * 处理文件来源
     *
     * @param inputSource 上传的文件
     * @param file
     */
    protected abstract void processFile(Object inputSource, File file) throws IOException;

    /**
     * 封装压缩图的返回结果
     *
     * @param originalFilename  原始文件名
     * @param compressCiObject  压缩后的图片对象
     * @param thumbnailCiObject 缩略图的图片对象
     * @param imageInfo         图片对象信息
     * @return UploadPictureResult
     */
    private UploadPictureResult buildResult(String originalFilename, CIObject compressCiObject,
                                            CIObject thumbnailCiObject, ImageInfo imageInfo, Object inputSource) {
        // 计算宽高和宽高比
        int picWidth = compressCiObject.getWidth();
        int picHeight = compressCiObject.getHeight();
        double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();

        // 封装返回结果
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        // 设置压缩后的原图地址
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + compressCiObject.getKey());
        uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(compressCiObject.getSize().longValue());
        uploadPictureResult.setPicWidth(picWidth);
        uploadPictureResult.setPicHeight(picHeight);
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(compressCiObject.getFormat());
        uploadPictureResult.setPicColor(imageInfo.getAve());
        // 设置缩略图地址
        uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
        // 如果inputSource是String类型，则设置originalUrl
        if (inputSource instanceof String) {
            uploadPictureResult.setOriginalUrl(inputSource.toString());
        } else {
            // 否则设置为压缩图的地址
            uploadPictureResult.setOriginalUrl(cosClientConfig.getHost() + "/" + compressCiObject.getKey());
        }
        // 返回可访问的地址
        return uploadPictureResult;
    }

    /**
     * 封装返回结果
     *
     * @param originalFilename
     * @param file
     * @param uploadPath
     * @param imageInfo        图片对象信息
     * @param inputSource
     * @return
     */
    private UploadPictureResult buildResult(String originalFilename, File file, String uploadPath, ImageInfo imageInfo, Object inputSource) {
        // 计算宽高和宽高比
        int picWidth = imageInfo.getWidth();
        int picHeight = imageInfo.getHeight();
        double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();

        // 封装返回结果
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + uploadPath);
        uploadPictureResult.setOriginalUrl(uploadPath);
        uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(FileUtil.size(file));
        uploadPictureResult.setPicWidth(picWidth);
        uploadPictureResult.setPicHeight(picHeight);
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(imageInfo.getFormat());
        uploadPictureResult.setPicColor(imageInfo.getAve());
        // 如果inputSource是String类型，则设置originalUrl
        if (inputSource instanceof String) {
            uploadPictureResult.setOriginalUrl(inputSource.toString());
        } else {
            // 否则设置为压缩图的地址
            uploadPictureResult.setOriginalUrl(cosClientConfig.getHost() + "/" + uploadPath);
        }
        // 返回可访问的地址
        return uploadPictureResult;
    }

    /**
     * 删除临时文件
     *
     * @param file 临时文件
     */
    private static void deleteTempFile(File file) {
        if (file != null) {
            // 删除临时文件
            if (!file.delete()) {
                log.warn("临时文件删除失败，filePath = {}", file.getAbsolutePath());
            }
        }
    }
}
